﻿using PageAdmin.Utils.ORM;
using PageAdmin.Utils.ORM.PropertyDescriptor;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace PageAdmin.Utils.BLL
{
    /// <summary>
    ///附表数据（子表)处理逻辑
    /// </summary>
    public class SubdataBLL
    {
        private static MethodInfo GetSubdataListMethod;
        private static MethodInfo GetSubdataMethod;
        static SubdataBLL()
        {
            //静态保存，避免每次反射查找
            GetSubdataListMethod = typeof(SubdataBLL).GetMethod("GetSubdataList", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(DataBaseContext), typeof(string), typeof(int), typeof(string), typeof(string) }, null);
            GetSubdataMethod = typeof(SubdataBLL).GetMethod("GetSubdata", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(DataBaseContext), typeof(string), typeof(int), typeof(string), typeof(string) }, null);
        }
        /// <summary>
        /// 添加或编辑附属表数据
        /// </summary>
        /// <param name="tModel"></param>
        /// <param name="detailId"></param>
        /// <param name="tipsInfo"></param>
        /// <returns></returns>
        public static TipsInfo AddOrEditSubdata<T>(DataBaseContext dbContext, T tModel, int detailId, string method) where T : class, new()
        {
            TipsInfo tipsInfo = new TipsInfo();
            IEnumerable<ModelFieldMapped> modelFieldMappedList = dbContext.DbSet<T>().ModelFieldMappingList.Where(c => c.IsSubDataMapped == true);
            foreach (ModelFieldMapped modelFieldMapped in modelFieldMappedList)
            {
                if (method == "add" && !modelFieldMapped.SubdataMappedDescriptor.SyncAdd)
                {
                    continue;
                }
                else if (method == "edit" && !modelFieldMapped.SubdataMappedDescriptor.SyncEdit)
                {
                    continue;
                }
                PropertyInfo proInfo = modelFieldMapped.PropertyInfo;
                if (proInfo == null)
                { //找不到属性
                    continue;
                }
                object propertyValue = proInfo.GetValue(tModel, null);
                if (propertyValue == null)
                { //属性值为Null则跳过
                    continue;
                }
                string foreignKey = modelFieldMapped.SubdataMappedDescriptor.ForeignKey;//关联的外键
                string subTableName = modelFieldMapped.SubdataMappedDescriptor.SubtableName;
                Type subtableType = modelFieldMapped.SubdataMappedDescriptor.SubmodelType;

                HashSet<int> newsIdList = new HashSet<int>();//编辑时保留的id，用来判断编辑状态那些数据保留了，那些数据已删除。
                HashSet<int> existsIdList = new HashSet<int>(); //编辑前数据库中存在的子表id集合
                if (method == "edit")//先删除子数据
                {
                    dbContext.AddDbParamter("detailId", detailId);
                    //获取已经存在的子表数据
                   foreach(var item in dbContext.Query(subTableName, "[Id]", $"{foreignKey}=@detailId"))
                    {
                        existsIdList.Add((int)item.Id);
                    }
                }

                IEnumerable<dynamic> subdataList;
                if (modelFieldMapped.IsIEnumerable) //属性是一对多
                {
                    subdataList = ((IEnumerable<dynamic>)((dynamic)propertyValue));
                    foreach (dynamic item in subdataList)
                    {
                        if (item != null)
                        {
                            tipsInfo = AddOrEditSubdataItem(dbContext, existsIdList,newsIdList, item, modelFieldMapped, detailId, tipsInfo, method);
                            if (tipsInfo.State == 0){
                                return tipsInfo;
                            }
                        }
                    }
                }
                else //属性是一对一
                {
                    tipsInfo = AddOrEditSubdataItem(dbContext, existsIdList, newsIdList, (dynamic)propertyValue, modelFieldMapped, detailId, tipsInfo, method);
                    if (tipsInfo.State == 0){
                        return tipsInfo;
                    }
                }
                if (method == "edit")//删除已经存在的冗余子数据，在编辑状态时被删除
                {

                    if (existsIdList.Count()>0)
                    {
                        Type deleteType = typeof(DeleteBLL<>); //泛型Dll
                        Type[] genericTypeParmter = new[] { subtableType }; //泛型dll的泛型参数类型
                        Type genericType = deleteType.MakeGenericType(genericTypeParmter);//创建泛型的Delete泛型类
                        MethodInfo deleteMethod = genericType.GetMethod("Delete", new Type[] { typeof(int) });
                        var obj = Activator.CreateInstance(genericType, new object[] { dbContext });//根据类型创建Delete实例。

                        foreach (int itemId in existsIdList)
                        {
                            if (!newsIdList.Contains(itemId)) //没有保留的则删除
                            {
                                tipsInfo= (TipsInfo)deleteMethod.Invoke(obj, new object[] { itemId });
                                if (tipsInfo.State == 0)
                                {
                                    return tipsInfo;
                                }
                            }
                        }
                    }
                }
            }
            return tipsInfo;
        }


        /// <summary>
        /// 附属表中添加一条记录,或对现有记录进行编辑
        /// </summary>
        /// <typeparam name="subModelType"></typeparam>
        /// <param name="dbContext"></param>
        /// <param name="existsList"></param>
        /// <param name="subModel"></param>
        /// <param name="modelFieldMapped"></param>
        /// <param name="newId"></param>
        /// <param name="tipsInfo"></param>
        /// <returns></returns>
        private static TipsInfo AddOrEditSubdataItem<subModelType>(DataBaseContext dbContext, HashSet<int> existsList, HashSet<int> newsIdList, subModelType subModel, ModelFieldMapped modelFieldMapped, int newId, TipsInfo tipsInfo,string method) where subModelType : class, new()
        {
            //获取外键属性元数据
            PropertyInfo foreignKeyProInfo = modelFieldMapped.SubdataMappedDescriptor.ForeignKeyPropertyInfo; //子表的外键
            if (foreignKeyProInfo != null)
            {
                foreignKeyProInfo.SetValue(subModel, newId, null); //外键设置值
                if (method=="add")
                {
                    IAdd<subModelType> addBll = new AddBLL<subModelType>(dbContext);
                    tipsInfo = addBll.Add(subModel);
                }
                else
                {
                    PropertyInfo primaryKeyProInfo = modelFieldMapped.SubdataMappedDescriptor.PrimaryKeyPropertyInfo; //子表的主键id
                    int id = (int)primaryKeyProInfo.GetValue(subModel, null); //子表的id
                    if (id>0 && existsList.Contains(id))   //existsList.Contains检测，避免伪造id造成对不相关的id进行编辑
                    {
                        newsIdList.Add(id);//已经保留的
                        IEdit<subModelType> editBll = new EditBLL<subModelType>(dbContext);
                        tipsInfo = editBll.Edit(subModel);
                    }
                    else
                    {
                        IAdd<subModelType> addBll = new AddBLL<subModelType>(dbContext);
                        tipsInfo = addBll.Add(subModel);

                    }
                }
              
            }
            else
            {
                tipsInfo.Msg = $"外键{modelFieldMapped.SubdataMappedDescriptor.ForeignKey}未定义!";
                tipsInfo.State = 0;
            }
            return tipsInfo;
        }


        /// <summary>
        /// 设置中间数据的属性值
        /// </summary>
        /// <param name="goodModelId"></param>
        /// <returns></returns>
        public static void SetSubdata<T>(DataBaseContext dbContext, T tModel, int id, bool isDetail = true) where T : class, new()
        {
            TipsInfo tipsInfo = new TipsInfo();
            DbSet<T> dbSet = dbContext.DbSet<T>();
            IEnumerable<ModelFieldMapped> modelFieldMappedList = dbSet.ModelFieldMappingList.Where(c => c.IsSubDataMapped == true);
            if (isDetail)
            {
                modelFieldMappedList = modelFieldMappedList.Where(c => c.SubdataMappedDescriptor.DetailSyncLoad == true);
            }
            else
            {
                modelFieldMappedList = modelFieldMappedList.Where(c => c.SubdataMappedDescriptor.ListSyncLoad == true);
            }
            if (id <= 0)
            {
                return;
            }
            foreach (ModelFieldMapped modelFieldMapped in modelFieldMappedList)
            {
                var submodelType = modelFieldMapped.SubdataMappedDescriptor.SubmodelType;
                string foreignKey = modelFieldMapped.SubdataMappedDescriptor.ForeignKey; //获取子表外键
                string sqlCondition = modelFieldMapped.SubdataMappedDescriptor.SqlCondition;//获取自定义条件
                string orderBy = modelFieldMapped.SubdataMappedDescriptor.OrderBy;//获取排序
                if (modelFieldMapped.IsIEnumerable) //属性是列表
                {
                    MethodInfo gerericMethodInfo = GetSubdataListMethod.MakeGenericMethod(submodelType);
                    var value = gerericMethodInfo.Invoke(null, new object[] { dbContext, foreignKey, id, sqlCondition, orderBy });
                    modelFieldMapped.PropertyInfo.SetValue(tModel, value); //给属性赋值
                }
                else
                {
                    MethodInfo gerericMethodInfo = GetSubdataMethod.MakeGenericMethod(submodelType);
                    var value = gerericMethodInfo.Invoke(null, new object[] { dbContext, foreignKey, id, sqlCondition, orderBy });
                    modelFieldMapped.PropertyInfo.SetValue(tModel, value); //给属性赋值
                }

            }
        }

        /// <summary>
        /// 获取一对一的子表数据
        /// </summary>
        /// <typeparam name="subModelType"></typeparam>
        /// <param name="subModel"></param>
        private static subModelType GetSubdata<subModelType>(DataBaseContext dbContext, string foreignKey, int parentId, string sqlCondition, string orderBy) where subModelType : class, new()
        {
            IGetDetailData<subModelType> detailBLL = new GetDetailDataBLL<subModelType>(dbContext);
            if (!string.IsNullOrEmpty(sqlCondition))
            {
                sqlCondition = $"{foreignKey}= @parentId and {sqlCondition}";
            }
            else
            {
                sqlCondition = $"{foreignKey}= @parentId";
            }
            return detailBLL.GetData(sqlCondition, orderBy, new { parentId = parentId });
        }

        /// <summary>
        /// 获取一对多的子表数据
        /// </summary>
        /// <typeparam name="subModelType"></typeparam>
        /// <param name="subModel"></param>
        private static IEnumerable<subModelType> GetSubdataList<subModelType>(DataBaseContext dbContext, string foreignKey, int parentId,string sqlCondition,string orderBy) where subModelType : class, new()
        {
            IGetListData<subModelType> listBLL = new GetListDataBLL<subModelType>(dbContext);
            if (!string.IsNullOrEmpty(sqlCondition))
            {
                listBLL.QueryCondition = $"{foreignKey}= @parentId and {sqlCondition}";
            }else
            {
                listBLL.QueryCondition = $"{foreignKey}= @parentId";
            }
            listBLL.QueryDbParameters.Add("parentId", parentId);
            listBLL.OrderBy = orderBy;
            return listBLL.GetList(null);
        }

    }
}